package site.yunnong.atvris.portal.schedule;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import site.yunnong.atvris.common.dto.VideoQueryParam;
import site.yunnong.atvris.common.enumeration.MediaStatusEnum;
import site.yunnong.atvris.common.enumeration.MediaTypeEnum;
import site.yunnong.atvris.common.utils.RedisKeyUtils;
import site.yunnong.atvris.dao.domain.SysLogExample;
import site.yunnong.atvris.dao.domain.UserProfile;
import site.yunnong.atvris.dao.domain.VideoProfile;
import site.yunnong.atvris.dao.domain.ext.VideoDetail;
import site.yunnong.atvris.dao.mapper.SysLogMapper;
import site.yunnong.atvris.dao.mapper.ext.VideoExtMapper;
import site.yunnong.atvris.service.*;
import site.yunnong.atvris.service.helper.ScoreHelper;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;

/**
 * @author zjh
 * @date 2021/9/10 0:30
 */
@Slf4j
@Component
@EnableScheduling
public class ScheduleContext {
    
    @Resource
    private VideoProfileService videoProfileService;
    @Resource
    private VideoExtMapper videoExtMapper;
    @Resource
    private UserProfileService userProfileService;
    @Resource
    private InteractionService interactionService;
    @Resource
    private SysLogMapper sysLogMapper;
    @Resource
    private EsVideoService esVideoService;
    @Resource
    private EsUserService esUserService;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private VideoService videoService;

    /**
     * 每1个小时执行一次持久化
     * 保存video profile 信息
     */
    @Scheduled(cron = "0 0 0/1 * * ? ")
    public void persistentVideoProfile() {
        log.info("ScheduleContext.persistentVideoProfile...");
        List<VideoProfile> videoProfiles = videoProfileService.queryAll();
        if (CollectionUtils.isNotEmpty(videoProfiles)) {
            for (VideoProfile videoProfile : videoProfiles) {
                boolean isChanged = confirmVideoProfileChanged(videoProfile);
                if (isChanged) {
                    videoProfileService.updateVideoProfile(videoProfile);
                }
            }
        }
        log.info("ScheduleContext.persistentVideoProfile finished.");
    }

    private boolean confirmVideoProfileChanged(VideoProfile videoProfile) {
        Integer mediaType = MediaTypeEnum.VIDEO.getValue();
        int newExposureCount = interactionService.findMediaExposureCount(mediaType, videoProfile.getUvid());
        int newClickCount = interactionService.findMediaClickCount(mediaType, videoProfile.getUvid());
        int newViewCount = interactionService.findMediaViewCount(mediaType, videoProfile.getUvid());
        int newLikedCount = interactionService.findMediaLikedCount(mediaType, videoProfile.getUvid());
        // 确认是否变更
        boolean isExposureChanged = !videoProfile.getExposureCount().equals(newExposureCount);
        boolean isClickChanged = !videoProfile.getClickCount().equals(newClickCount);
        boolean isViewChanged = !videoProfile.getViewCount().equals(newViewCount);
        boolean isLikedChanged = !videoProfile.getLikedCount().equals(newLikedCount);
        // set
        videoProfile.setExposureCount(newExposureCount);
        videoProfile.setClickCount(newClickCount);
        videoProfile.setViewCount(newViewCount);
        videoProfile.setLikedCount(newLikedCount);
        return isExposureChanged || isClickChanged || isViewChanged || isLikedChanged;
    }

    /**
     * 每1个小时执行一次持久化
     * 保存user profile 信息
     */
    @Scheduled(cron = "0 0 0/1 * * ? ")
    public void persistentUserProfile() {
        log.info("ScheduleContext.persistentUserProfile...");
        List<UserProfile> userProfiles = userProfileService.queryAll();
        if (CollectionUtils.isNotEmpty(userProfiles)) {
            for (UserProfile userProfile : userProfiles) {
                boolean isChanged = confirmUserProfileChanged(userProfile);
                if (isChanged) {
                    userProfileService.updateUserProfile(userProfile);
                }
            }
        }
        log.info("ScheduleContext.persistentUserProfile finished.");
    }

    private boolean confirmUserProfileChanged(UserProfile userProfile) {
        int newFollowCount = (int) interactionService.findUserFollowCount(userProfile.getUuid());
        int newFansCount = (int) interactionService.findUserFansCount(userProfile.getUuid());
        int newLikedCount = interactionService.findUserLikedCount(userProfile.getUuid());
        int newViewCount = interactionService.findUserViewCount(userProfile.getUuid());
        
        // 确认是否变更
        boolean isFollowCountChanged = !userProfile.getFollowCount().equals(newFollowCount);
        boolean isFansCountChanged = !userProfile.getFansCount().equals(newFansCount);
        boolean isViewChanged = !userProfile.getViewCount().equals(newViewCount);
        boolean isLikedChanged = !userProfile.getLikedCount().equals(newLikedCount);
        // set
        userProfile.setFollowCount(newFollowCount);
        userProfile.setFansCount(newFansCount);
        userProfile.setViewCount(newViewCount);
        userProfile.setLikedCount(newLikedCount);
        return isFollowCountChanged || isFansCountChanged || isViewChanged || isLikedChanged;
    }

    /**
     * 定时清除一个月前的日志
     */
    @Scheduled(cron = "0 0 0 * * ? ")
    public void removeSysLog() {
        SysLogExample example = new SysLogExample();
        example.createCriteria().andCreateDateLessThan(LocalDateTime.now().minusMonths(1));
        sysLogMapper.deleteByExample(example);
    }

    /**
     * 定时任务刷新es
     */
    @Scheduled(cron = "0 0 0 * * ? ")
    public void refreshEs() {
        esVideoService.importAll();
        esUserService.importAll();
    }

    /**
     * 定时任务,五分钟刷新一次排名
     */
    @Scheduled(cron = "0 0/5 * * * ?")
    public void refreshScore() {
        log.info("刷新视频评分...");
        String mediaScoreKey = RedisKeyUtils.getMediaScoreKey(MediaTypeEnum.VIDEO.getValue());
        BoundSetOperations boundSetOperations = redisTemplate.boundSetOps(mediaScoreKey);
        if (boundSetOperations.size() == 0) {
            log.info("没有需要刷新评分的视频");
            return;
        }
        while(boundSetOperations.size() > 0) {
            refreshScore((Long)boundSetOperations.pop());
        }
        log.info("刷新视频评分完成");
    }
    
    private void refreshScore(Long uvid) {
        VideoQueryParam param = new VideoQueryParam();
        param.setUvid(uvid);
        param.setStatus(MediaStatusEnum.APPROVED.getValue());
        List<VideoDetail> videoDetails = videoExtMapper.queryUserVideoList(param);
        if (CollectionUtils.isEmpty(videoDetails)) {
            return;
        }
        VideoDetail videoDetail = videoDetails.get(0);
        Integer exposureCount = interactionService.findMediaExposureCount(MediaTypeEnum.VIDEO.getValue(), uvid);
        Integer clickCount = interactionService.findMediaClickCount(MediaTypeEnum.VIDEO.getValue(), uvid);
        Integer viewCount = interactionService.findMediaViewCount(MediaTypeEnum.VIDEO.getValue(), uvid);
        Integer likedCount = interactionService.findMediaLikedCount(MediaTypeEnum.VIDEO.getValue(), uvid);
        videoDetail.setExposureCount(exposureCount);
        videoDetail.setClickCount(clickCount);
        videoDetail.setViewCount(viewCount);
        videoDetail.setLikedCount(likedCount);
        // 计算评分
        Double score = ScoreHelper.calcScoreHackerNews(videoDetail);
        videoDetail.setScore(score);
        VideoProfile videoProfile = new VideoProfile();
        BeanUtils.copyProperties(videoDetail, videoProfile);
        videoProfileService.updateVideoProfile(videoProfile);
        // 刷新es
        esVideoService.save(uvid);
    }
}
